package com.tdking.upload.config;


import com.alibaba.fastjson.JSON;
import com.qiqi.common.enumerator.ServiceCode;
import com.qiqi.common.web.JsonResult;
import com.qiqi.upload.fifter.JwtAuthorizationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.io.PrintWriter;


@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthorizationFilter jwtAuthorizationFilter;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 跨域访问
        http.cors();

        // 配置Spring Security创建Session的策略，配置为：不使用Session
        // 关于取值：
        // -- NEVER：从不主动创建Session，但是，如果Session已存在，将会使用
        // -- STATELESS：无状态的，永不使用Session保存客户端的状态
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 将解析JWT的过滤器，配置在Spring Security的相关过滤器之前
        http.addFilterBefore(jwtAuthorizationFilter, UsernamePasswordAuthenticationFilter.class);

        http.cors();

        // 处理“需要认证却尚未通过认证”的问题
        http.exceptionHandling().authenticationEntryPoint((request, response, e) -> {
            String message = "您当前未登录，请先登录！";
            JsonResult jsonResult = JsonResult.fail(ServiceCode.ERROR_UNAUTHORIZED, message);
            String jsonString = JSON.toJSONString(jsonResult);

            response.setContentType("application/json; charset=UTF-8");
            PrintWriter writer = response.getWriter();
            writer.println(jsonString);
            writer.close();
        });

        // 禁用“防止伪造的跨域攻击的防御机制”
        http.csrf().disable();

        // 白名单
        // 在配置请求路径时，可以使用通配符
        // -- 1个星号，可以匹配某层级下的任意资源，例如配置为 /users/*，可以匹配 /users/login 或 /users/9527，但不可以匹配 /users/9527/disable
        // -- 2个星号，可以匹配任意层级下的任意资源，例如配置为 /users/**，可以匹配 /users/login 或 /users/9527，也可以匹配 /users/9527/disable
        String[] urls = {
                "/doc.html",
                "/**/*.css",
                "/**/*.js",
                "/favicon.ico",
                "/swagger-resources",
                "/v2/api-docs",
                "/passport/login",
        };

        // 请求授权
        // 各配置遵循“第一匹配原则”，即：如果某个请求被多个配置都可以匹配，以第1次匹配的为准
        // 在配置时，应该先配置更精准匹配的请求，再配置较模糊的
        http.authorizeRequests()
                .mvcMatchers(urls) // 匹配某些请求
                .permitAll() // 许可，即不需要认证即可访问
                .anyRequest() // 匹配任何请求，即所有请求，具体表现为：除了以上配置过的请求以外的其它请求
                .authenticated() // 需要已经通过认证
        ;

        // 当调用formLogin()时，表示启用登录页与登出页
        // 当尝试请求某个需要通过认证的资源，却还没有通过认证时：
        // -- 如果启用登录页，则会自动重定向到登录页
        // -- 如果未启用登录页，则会响应403错误
        // http.formLogin();
    }

}
